package com.xiaohua.api.controller;

import com.xiaohua.common.core.domain.AjaxResult;
import com.xiaohua.common.core.controller.BaseController;
import com.xiaohua.common.annotation.Log;
import com.xiaohua.common.enums.BusinessType;
import com.xiaohua.common.utils.StringUtils;
import com.xiaohua.common.utils.file.FileUtils;
import com.xiaohua.common.utils.ServletUtils;

import com.xiaohua.api.service.IXhDocumentService;
import com.xiaohua.common.service.ITokenService;
import com.xiaohua.common.core.domain.model.LoginUser;
import com.xiaohua.api.service.IOperationLogService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.beans.factory.annotation.Qualifier;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.http.MediaType;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
import java.util.HashMap;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;

/**
 * 文档转换控制器
 *
 * @author 小花
 * @date 2025-03-29
 */
@Api(tags = "文档转换接口", description = "提供PDF转Word、图片转Word等多种文档格式转换功能")
@RestController
@RequestMapping("/api/document")
public class XhDocumentController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(XhDocumentController.class);

    @Autowired
    private IXhDocumentService xhDocumentService;

    @Autowired
    @Qualifier("xhApiTokenServiceImpl")
    private ITokenService tokenService;
    
    @Autowired
    private IOperationLogService operationLogService;

    /**
     * API功能接口，用于封装API调用逻辑
     */
    @FunctionalInterface
    private interface APIFunction {
        /**
         * 执行API调用
         *
         * @return API调用结果
         */
        AjaxResult execute() throws Exception;
    }

    /**
     * 验证用户token并在成功调用API后扣减token
     *
     * @param operationType 操作类型，用于日志记录
     * @param isMultiFile 是否多文件输出
     * @param apiMethod 要执行的API方法
     * @return API执行结果
     */
    private AjaxResult validateTokenAndExecute(String operationType, boolean isMultiFile, APIFunction apiMethod) {
        // 获取当前登录用户
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            log.warn("API调用失败：用户未登录");
            return AjaxResult.error("用户未登录，请先登录");
        }

        Long userId = loginUser.getUserId();
        String username = loginUser.getUsername();
        String requestURI = ServletUtils.getRequest().getRequestURI();
        log.info("用户[{}({})]请求API[{}]调用", username, userId, requestURI);

        // 验证用户是否有足够的tokens
        if (!tokenService.checkUserTokens(userId)) {
            log.warn("用户[{}({})]tokens不足，API[{}]调用被拒绝", username, userId, requestURI);
            // 记录操作日志 - 因tokens不足被拒绝
            operationLogService.logOperation(userId, operationType, 403);
            return AjaxResult.error("您的tokens已用完，请充值后再使用");
        }

        // 性能监控 - 记录开始时间
        long startTime = System.currentTimeMillis();

        // 执行API调用
        AjaxResult result;
        try {
            // 执行实际API调用
            result = apiMethod.execute();

            // 如果API调用成功，扣减用户tokens
            if (result.isSuccess()) {
                boolean deducted = tokenService.deductUserTokens(userId);
                if (deducted) {
                    log.info("用户[{}({})]API[{}]调用成功，tokens已扣减，耗时{}ms",
                            username, userId, requestURI, (System.currentTimeMillis() - startTime));
                    // 记录操作日志 - 成功
                    operationLogService.logOperationFromResult(userId, result, operationType, isMultiFile);
                } else {
                    // 记录严重错误：API调用成功但token扣减失败
                    log.error("用户[{}({})]API[{}]调用成功，但tokens扣减失败，耗时{}ms",
                            username, userId, requestURI, (System.currentTimeMillis() - startTime));
                    // 记录操作日志 - 成功但token扣减失败
                    operationLogService.logOperationFromResult(userId, result, operationType + "_但TOKEN扣减失败", isMultiFile);
                }
            } else {
                // API调用失败，不扣减tokens
                log.info("用户[{}({})]API[{}]调用失败(业务错误)，未扣减tokens，耗时{}ms",
                        username, userId, requestURI, (System.currentTimeMillis() - startTime));
                // 记录操作日志 - 业务失败
                operationLogService.logOperation(userId, operationType, 400);
            }
        } catch (Exception e) {
            // 系统异常处理
            long duration = System.currentTimeMillis() - startTime;
            log.error("用户[{}({})]API[{}]调用异常，耗时{}ms, 错误: {}",
                    username, userId, requestURI, duration, e.getMessage(), e);
            // 记录操作日志 - 系统异常
            operationLogService.logOperation(userId, operationType, 500);
            return AjaxResult.error("API调用失败: " + e.getMessage());
        }

        return result;
    }
    
    /**
     * 简化的validateTokenAndExecute方法，多数场景下不是多文件输出
     */
    private AjaxResult validateTokenAndExecute(String operationType, APIFunction apiMethod) {
        return validateTokenAndExecute(operationType, false, apiMethod);
    }

    /**
     * 处理单文件输入并执行相应的API方法
     *
     * @param file            文件参数
     * @param fileBase64      Base64编码参数
     * @param fileUrl         文件URL参数
     * @param fileProcessor   文件处理器，处理上传的文件
     * @param base64Processor Base64处理器，处理Base64字符串
     * @param urlProcessor    URL处理器，处理URL字符串
     * @return API执行结果
     * @throws Exception 如果处理过程中出现错误
     */
    private AjaxResult processFileInput(
            MultipartFile file,
            String fileBase64,
            String fileUrl,
            ThrowingFunction<MultipartFile, AjaxResult> fileProcessor,
            ThrowingFunction<String, AjaxResult> base64Processor,
            ThrowingFunction<String, AjaxResult> urlProcessor) throws Exception {

        if (file != null) {
            return fileProcessor.apply(file);
        } else if (!StringUtils.isEmpty(fileBase64)) {
            return base64Processor.apply(fileBase64);
        } else {
            return urlProcessor.apply(fileUrl);
        }
    }

    /**
     * 处理多图片输入并执行相应的API方法
     *
     * @param files               多个文件参数
     * @param fileBase64List      多个Base64编码参数
     * @param imageUrls           多个URL参数
     * @param filesProcessor      文件列表处理器
     * @param base64ListProcessor Base64列表处理器
     * @param urlListProcessor    URL列表处理器
     * @return API执行结果
     * @throws Exception 如果处理过程中出现错误
     */
    private AjaxResult processMultipleImageInput(
            List<MultipartFile> files,
            List<String> fileBase64List,
            List<String> imageUrls,
            ThrowingFunction<List<MultipartFile>, AjaxResult> filesProcessor,
            ThrowingFunction<List<String>, AjaxResult> base64ListProcessor,
            ThrowingFunction<List<String>, AjaxResult> urlListProcessor) throws Exception {

        if (files != null && !files.isEmpty()) {
            return filesProcessor.apply(files);
        } else if (fileBase64List != null && !fileBase64List.isEmpty()) {
            return base64ListProcessor.apply(fileBase64List);
        } else {
            return urlListProcessor.apply(imageUrls);
        }
    }

    /**
     * 用于处理可能抛出异常的函数接口
     */
    @FunctionalInterface
    private interface ThrowingFunction<T, R> {
        R apply(T t) throws Exception;
    }

    /**
     * 记录异常的详细信息
     *
     * @param apiName    API名称
     * @param file       文件参数
     * @param fileBase64 Base64参数
     * @param fileUrl    URL参数
     * @param e          异常
     */
    private void logDetailedError(String apiName, MultipartFile file, String fileBase64, String fileUrl, Exception e) {
        String errorSource = file != null ? "文件" : (StringUtils.isNotEmpty(fileBase64) ? "Base64" : "URL");
        String sourceDetail = file != null ? file.getOriginalFilename() :
                (StringUtils.isNotEmpty(fileBase64) ? "Base64数据(长度:" + fileBase64.length() + ")" :
                        "URL:" + fileUrl);
        log.error("{}转换失败，来源类型:{}，来源:{}, 错误:{}", apiName, errorSource, sourceDetail, e.getMessage(), e);
    }

    /**
     * 记录多文件异常的详细信息
     */
    private void logMultiFileDetailedError(String apiName, List<MultipartFile> files, List<String> fileBase64List, List<String> imageUrls, Exception e) {
        String errorSource;
        String sourceDetail;

        if (files != null && !files.isEmpty()) {
            errorSource = "多文件";
            sourceDetail = "文件数量:" + files.size();
        } else if (fileBase64List != null && !fileBase64List.isEmpty()) {
            errorSource = "多Base64";
            sourceDetail = "Base64数量:" + fileBase64List.size();
        } else {
            errorSource = "多URL";
            sourceDetail = "URL数量:" + (imageUrls != null ? imageUrls.size() : 0);
        }

        log.error("{}转换失败，来源类型:{}，来源:{}, 错误:{}", apiName, errorSource, sourceDetail, e.getMessage(), e);
    }

    /**
     * PDF转Word
     */
    @ApiOperation(value = "PDF转Word", notes = "将PDF文档转换为可编辑的Word文档，支持三种方式提交PDF：1. PDF文件上传；2. Base64编码；3. PDF的URL。不支持1000页以上或100MB以上的PDF文件。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "PDF文件（二进制文件流）", dataTypeClass = MultipartFile.class, paramType = "form", example = "（文件二进制流）"),
            @ApiImplicitParam(name = "fileBase64", value = "PDF文件的Base64编码，仅在未提供file时有效", dataTypeClass = String.class, paramType = "form", example = "JVBERi0xLjQKJdP...（Base64编码字符串）"),
            @ApiImplicitParam(name = "fileUrl", value = "PDF文件的URL地址，仅在未提供file和fileBase64时有效", dataTypeClass = String.class, paramType = "form", example = "https://example.com/sample.pdf"),
            @ApiImplicitParam(name = "formulaEnhancement", value = "是否开启公式识别增强选项，对于包含数学公式的PDF建议开启", dataTypeClass = Boolean.class, paramType = "form", defaultValue = "false", example = "true")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功，返回任务ID，需要使用任务ID查询转换结果"),
            @ApiResponse(code = 500, message = "转换失败")
    })
    @Log(title = "PDF转Word", businessType = BusinessType.OTHER)
    @PostMapping(value = "/pdf-to-word", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult pdfToWord(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "fileUrl", required = false) String fileUrl,
            @RequestParam(value = "formulaEnhancement", required = false, defaultValue = "false") Boolean formulaEnhancement) {

        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(fileUrl)) {
            return AjaxResult.error("请提供PDF文件、Base64编码的PDF数据或PDF文件URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalFileUrl = fileUrl;
        final Boolean finalFormulaEnhancement = formulaEnhancement;

        return validateTokenAndExecute("PDF转Word", () -> {
            try {
                return processFileInput(finalFile, finalFileBase64, finalFileUrl,
                        file1 -> xhDocumentService.convertPdfToWord(file1, finalFormulaEnhancement),
                        base64 -> xhDocumentService.convertPdfToWordByBase64(base64, finalFormulaEnhancement),
                        url -> xhDocumentService.convertPdfToWordByUrl(url, finalFormulaEnhancement));
            } catch (Exception e) {
                logDetailedError("PDF转Word", finalFile, finalFileBase64, finalFileUrl, e);
                return AjaxResult.error("转换失败: " + e.getMessage());
            }
        });
    }

    /**
     * 图片转Word
     */
    @ApiOperation(value = "图片转Word", notes = "将图片转换为可编辑的Word文档，最多支持30张图片。支持的图片格式：jpg、jpeg、png、bmp、gif（gif仅支持第一帧）。不支持最长边在8192px以上或20MB以上的图片。")
    @Operation(summary = "图片转Word", description = "将图片转换为可编辑的Word文档，最多支持30张图片")
    @PostMapping(value = "/image-to-word", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public AjaxResult imageToWord(
            @Parameter(description = "图片文件列表", required = false)
            @RequestPart(value = "files", required = false) List<MultipartFile> files,
            @Parameter(description = "Base64编码的图片列表", required = false)
            @RequestParam(value = "fileBase64List", required = false) List<String> fileBase64List,
            @Parameter(description = "图片URL列表", required = false)
            @RequestParam(value = "imageUrls", required = false) List<String> imageUrls,
            @Parameter(description = "图片文件后缀", required = false, schema = @Schema(type = "string", allowableValues = {"jpg", "jpeg", "png", "bmp", "gif"}))
            @RequestParam(value = "imageNameExtension", required = false) String imageNameExtension) {

        // 检查参数，必须提供三种参数中的一种
        if ((files == null || files.isEmpty()) &&
                (fileBase64List == null || fileBase64List.isEmpty()) &&
                (imageUrls == null || imageUrls.isEmpty())) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 检查图片数量限制
        int imageCount = files != null ? files.size() :
                (fileBase64List != null ? fileBase64List.size() :
                        (imageUrls != null ? imageUrls.size() : 0));

        if (imageCount > 30) {
            return AjaxResult.error("图片数量超过限制，最多支持30张图片");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final List<MultipartFile> finalFiles = files;
        final List<String> finalFileBase64List = fileBase64List;
        final List<String> finalImageUrls = imageUrls;
        final String finalImageNameExtension = imageNameExtension;

        return validateTokenAndExecute("图片转Word", false, () -> {
            try {
                return processMultipleImageInput(finalFiles, finalFileBase64List, finalImageUrls,
                        fileList -> xhDocumentService.convertImageToWord(fileList, finalImageNameExtension),
                        base64List -> xhDocumentService.convertImageToWordByBase64(base64List, finalImageNameExtension),
                        urlList -> xhDocumentService.convertImageToWordByUrl(urlList, finalImageNameExtension));
            } catch (Exception e) {
                logMultiFileDetailedError("图片转Word", finalFiles, finalFileBase64List, finalImageUrls, e);
                return AjaxResult.error("转换失败: " + e.getMessage());
            }
        });
    }

    /**
     * PDF转图片
     */
    @ApiOperation(value = "PDF转图片", notes = "将PDF文档逐页转换为图片，返回的是包含多个图片URL的数组（每页一个图片）。不支持1000页以上或100MB以上的PDF文件。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "PDF文件（二进制文件流）", dataTypeClass = MultipartFile.class, paramType = "form", example = "（文件二进制流）"),
            @ApiImplicitParam(name = "fileBase64", value = "PDF文件的Base64编码，仅在未提供file时有效", dataTypeClass = String.class, paramType = "form", example = "JVBERi0xLjQKJdP...（Base64编码字符串）"),
            @ApiImplicitParam(name = "fileUrl", value = "PDF文件的URL地址，仅在未提供file和fileBase64时有效", dataTypeClass = String.class, paramType = "form", example = "https://example.com/sample.pdf")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功，返回任务ID，需要使用任务ID查询转换结果"),
            @ApiResponse(code = 500, message = "转换失败")
    })
    @Log(title = "PDF转图片", businessType = BusinessType.OTHER)
    @PostMapping(value = "/pdf-to-image", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult pdfToImage(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "fileUrl", required = false) String fileUrl) {

        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(fileUrl)) {
            return AjaxResult.error("请提供PDF文件、Base64编码的PDF数据或PDF文件URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalFileUrl = fileUrl;

        return validateTokenAndExecute("PDF转图片", true, () -> {
            try {
                return processFileInput(finalFile, finalFileBase64, finalFileUrl,
                        xhDocumentService::convertPdfToImage,
                        xhDocumentService::convertPdfToImageByBase64,
                        xhDocumentService::convertPdfToImageByUrl);
            } catch (Exception e) {
                logDetailedError("PDF转图片", finalFile, finalFileBase64, finalFileUrl, e);
                return AjaxResult.error("转换失败: " + e.getMessage());
            }
        });
    }

    /**
     * 图片转PDF
     */
    @ApiOperation(value = "图片转PDF", notes = "将多张图片按指定顺序合并为单个PDF文档，最多支持30张图片。支持的图片格式：jpg、jpeg、png、bmp、gif（gif仅支持第一帧）。不支持最长边在8192px以上或20MB以上的图片。")
    @Operation(summary = "图片转PDF", description = "将多张图片合并为PDF文档，最多支持30张图片")
    @PostMapping(value = "/image-to-pdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public AjaxResult imageToPdf(
            @Parameter(description = "图片文件列表", required = false)
            @RequestPart(value = "files", required = false) List<MultipartFile> files,
            @Parameter(description = "Base64编码的图片列表", required = false)
            @RequestParam(value = "fileBase64List", required = false) List<String> fileBase64List,
            @Parameter(description = "图片URL列表", required = false)
            @RequestParam(value = "imageUrls", required = false) List<String> imageUrls,
            @Parameter(description = "图片文件后缀", required = false, schema = @Schema(type = "string", allowableValues = {"jpg", "jpeg", "png", "bmp", "gif"}))
            @RequestParam(value = "imageNameExtension", required = false) String imageNameExtension) {

        // 检查参数，必须提供三种参数中的一种
        if ((files == null || files.isEmpty()) &&
                (fileBase64List == null || fileBase64List.isEmpty()) &&
                (imageUrls == null || imageUrls.isEmpty())) {
            return AjaxResult.error("请提供图片文件、Base64编码的图片数据或图片URL");
        }

        // 检查图片数量限制
        int imageCount = files != null ? files.size() :
                (fileBase64List != null ? fileBase64List.size() :
                        (imageUrls != null ? imageUrls.size() : 0));

        if (imageCount > 30) {
            return AjaxResult.error("图片数量超过限制，最多支持30张图片");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final List<MultipartFile> finalFiles = files;
        final List<String> finalFileBase64List = fileBase64List;
        final List<String> finalImageUrls = imageUrls;
        final String finalImageNameExtension = imageNameExtension;

        return validateTokenAndExecute("图片转PDF", false, () -> {
            try {
                return processMultipleImageInput(finalFiles, finalFileBase64List, finalImageUrls,
                        fileList -> xhDocumentService.convertImageToPdf(fileList, finalImageNameExtension),
                        base64List -> xhDocumentService.convertImageToPdfByBase64(base64List, finalImageNameExtension),
                        urlList -> xhDocumentService.convertImageToPdfByUrl(urlList, finalImageNameExtension));
            } catch (Exception e) {
                logMultiFileDetailedError("图片转PDF", finalFiles, finalFileBase64List, finalImageUrls, e);
                return AjaxResult.error("转换失败: " + e.getMessage());
            }
        });
    }

    /**
     * 查询文档转换结果
     */
    @ApiOperation(value = "查询文档转换结果", notes = "通过任务ID查询文档转换的结果。仅适用于PDF转Word和PDF转图片任务，这两种任务依赖阿里云异步处理。其他本地处理的任务不支持查询结果。建议每10秒轮询一次，最多轮询120分钟。处理完成后的结果保留24小时。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "taskId", value = "文档转换任务ID，从转换接口返回结果中获取，必须以'docmind-'开头", required = true, dataTypeClass = String.class, paramType = "query", example = "docmind-20220712-b15f2a3c")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "查询成功，返回处理结果或处理状态"),
            @ApiResponse(code = 500, message = "查询失败")
    })
    @GetMapping("/result")
    public AjaxResult getConvertResult(@RequestParam("taskId") String taskId) {
        if (StringUtils.isEmpty(taskId)) {
            return AjaxResult.error("任务ID不能为空");
        }

        return validateTokenAndExecute("查询文档转换结果", () -> {
            try {
                return xhDocumentService.getDocumentConvertResult(taskId);
            } catch (Exception e) {
                log.error("查询文档转换结果失败，任务ID:{}，错误:{}", taskId, e.getMessage(), e);
                return AjaxResult.error("查询转换结果失败: " + e.getMessage());
            }
        });
    }

    /**
     * Word转PDF
     */
    @ApiOperation(value = "Word转PDF", notes = "将Word文档转换为PDF格式。支持的Word格式：doc、docx。不支持100MB以上的Word文件。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "Word文件（二进制文件流）", dataTypeClass = MultipartFile.class, paramType = "form", example = "（文件二进制流）"),
            @ApiImplicitParam(name = "fileBase64", value = "Word文件的Base64编码，仅在未提供file时有效", dataTypeClass = String.class, paramType = "form", example = "UEsDBBQABgAIAAAA...（Base64编码字符串）"),
            @ApiImplicitParam(name = "fileUrl", value = "Word文件的URL地址，仅在未提供file和fileBase64时有效", dataTypeClass = String.class, paramType = "form", example = "https://example.com/sample.docx")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功，直接返回转换后的PDF文件URL"),
            @ApiResponse(code = 500, message = "转换失败")
    })
    @Log(title = "Word转PDF", businessType = BusinessType.OTHER)
    @PostMapping(value = "/word-to-pdf", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult wordToPdf(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "fileUrl", required = false) String fileUrl) {

        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(fileUrl)) {
            return AjaxResult.error("请提供Word文件、Base64编码的Word数据或Word文件URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalFileUrl = fileUrl;

        return validateTokenAndExecute("Word转PDF", () -> {
            try {
                return processFileInput(finalFile, finalFileBase64, finalFileUrl,
                        xhDocumentService::convertWordToPdf,
                        xhDocumentService::convertWordToPdfByBase64,
                        xhDocumentService::convertWordToPdfByUrl);
            } catch (Exception e) {
                logDetailedError("Word转PDF", finalFile, finalFileBase64, finalFileUrl, e);
                return AjaxResult.error("转换失败: " + e.getMessage());
            }
        });
    }

    /**
     * Word转图片
     */
    @ApiOperation(value = "Word转图片", notes = "将Word文档逐页转换为图片，返回的是包含多个图片URL的数组（每页一个图片）。支持的Word格式：doc、docx。不支持100MB以上的Word文件。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "Word文件（二进制文件流）", dataTypeClass = MultipartFile.class, paramType = "form", example = "（文件二进制流）"),
            @ApiImplicitParam(name = "fileBase64", value = "Word文件的Base64编码，仅在未提供file时有效", dataTypeClass = String.class, paramType = "form", example = "UEsDBBQABgAIAAAA...（Base64编码字符串）"),
            @ApiImplicitParam(name = "fileUrl", value = "Word文件的URL地址，仅在未提供file和fileBase64时有效", dataTypeClass = String.class, paramType = "form", example = "https://example.com/sample.docx")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功，直接返回转换后的图片URL列表"),
            @ApiResponse(code = 500, message = "转换失败")
    })
    @Log(title = "Word转图片", businessType = BusinessType.OTHER)
    @PostMapping(value = "/word-to-image", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult wordToImage(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "fileUrl", required = false) String fileUrl) {

        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(fileUrl)) {
            return AjaxResult.error("请提供Word文件、Base64编码的Word数据或Word文件URL");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalFileUrl = fileUrl;

        return validateTokenAndExecute("Word转图片", true, () -> {
            try {
                return processFileInput(finalFile, finalFileBase64, finalFileUrl,
                        xhDocumentService::convertWordToImage,
                        xhDocumentService::convertWordToImageByBase64,
                        xhDocumentService::convertWordToImageByUrl);
            } catch (Exception e) {
                logDetailedError("Word转图片", finalFile, finalFileBase64, finalFileUrl, e);
                return AjaxResult.error("转换失败: " + e.getMessage());
            }
        });
    }

    /**
     * 文档加密
     */
    @ApiOperation(value = "文档加密", notes = "对Word或PDF文档添加密码保护。支持的文档格式：doc、docx、pdf。不支持100MB以上的文档文件。")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文档文件（二进制文件流）", dataTypeClass = MultipartFile.class, paramType = "form", example = "（文件二进制流）"),
            @ApiImplicitParam(name = "fileBase64", value = "文档文件的Base64编码，仅在未提供file时有效", dataTypeClass = String.class, paramType = "form", example = "文件的Base64编码字符串"),
            @ApiImplicitParam(name = "fileUrl", value = "文档文件的URL地址，仅在未提供file和fileBase64时有效", dataTypeClass = String.class, paramType = "form", example = "https://example.com/sample.docx"),
            @ApiImplicitParam(name = "password", value = "设置的密码", required = true, dataTypeClass = String.class, paramType = "form", example = "your_password"),
            @ApiImplicitParam(name = "ownerPassword", value = "文档所有者密码（PDF专用，具有完全权限），留空则与用户密码相同", dataTypeClass = String.class, paramType = "form", example = "owner_password")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "请求成功，直接返回加密后的文件URL"),
            @ApiResponse(code = 500, message = "加密失败")
    })
    @Log(title = "文档加密", businessType = BusinessType.OTHER)
    @PostMapping(value = "/document-encrypt", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public AjaxResult documentEncrypt(
            @RequestPart(value = "file", required = false) MultipartFile file,
            @RequestParam(value = "fileBase64", required = false) String fileBase64,
            @RequestParam(value = "fileUrl", required = false) String fileUrl,
            @RequestParam(value = "password", required = true) String password,
            @RequestParam(value = "ownerPassword", required = false) String ownerPassword) {

        // 检查参数，必须提供三种参数中的一种
        if (file == null && StringUtils.isEmpty(fileBase64) && StringUtils.isEmpty(fileUrl)) {
            return AjaxResult.error("请提供文档文件、Base64编码的文档数据或文档文件URL");
        }

        // 检查密码
        if (StringUtils.isEmpty(password)) {
            return AjaxResult.error("请提供加密密码");
        }

        // 为了避免Lambda表达式中的变量作用域问题
        final MultipartFile finalFile = file;
        final String finalFileBase64 = fileBase64;
        final String finalFileUrl = fileUrl;
        final String finalPassword = password;
        final String finalOwnerPassword = StringUtils.isEmpty(ownerPassword) ? password : ownerPassword;

        return validateTokenAndExecute("文档加密", () -> {
            try {
                return processFileInput(finalFile, finalFileBase64, finalFileUrl,
                        file1 -> xhDocumentService.encryptDocument(file1, finalPassword, finalOwnerPassword),
                        base64 -> xhDocumentService.encryptDocumentByBase64(base64, finalPassword, finalOwnerPassword),
                        url -> xhDocumentService.encryptDocumentByUrl(url, finalPassword, finalOwnerPassword));
            } catch (Exception e) {
                logDetailedError("文档加密", finalFile, finalFileBase64, finalFileUrl, e);
                return AjaxResult.error("加密失败: " + e.getMessage());
            }
        });
    }
} 